package com.youtills.core.db;

import com.youtills.core.config.Configuration;
import com.youtills.core.config.ConfigurationLoader;
import com.youtills.core.lang.StringUtil;
import com.youtills.core.lang.YoutillsRuntimeException;
import com.youtills.core.logging.Logger;
import org.apache.commons.dbcp.BasicDataSource;
//import org.apache.commons.dbcp.BasicDataSource;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by IntelliJ IDEA.
 * User: ravi
 * Date: 8/24/12
 * Time: 9:27 PM
 * To change this template use File | Settings | File Templates.
 */
public class YoutillsDataSource extends BasicDataSource implements DataSource {

    private static final Logger LOGGER = Logger.getLogger(YoutillsDataSource.class);

    static{
//        ConfigLoader.loadProperties("database.properties", false);
        ConfigurationLoader.loadProperties("database.properties", false);
    }

    private static final Map<DataSourceType, YoutillsDataSource> DATA_SOURCES = Collections.synchronizedMap(new HashMap<DataSourceType, YoutillsDataSource>());

    private final DataSourceType dataSourceType;

    public synchronized static YoutillsDataSource getDataSource(DataSourceType dataSourceType){

        if( dataSourceType != null){
            YoutillsDataSource dataSource = DATA_SOURCES.get(dataSourceType);
            if(dataSource != null){
                return dataSource;
            }
            return new YoutillsDataSource(dataSourceType);
        }
        return null;
    }

    private YoutillsDataSource(DataSourceType dataSourceType){

        showConnection();

        this.dataSourceType = dataSourceType;
        if(this.dataSourceType == null){
            throw new YoutillsRuntimeException("data source type and it's key cannot be null.. please check data source type : ", this.dataSourceType);
        }

        String key = dataSourceType.name().toLowerCase();

        setDriverClassName(StringUtil.firstNonEmpty(getProperty(key, "driverClassName"), "net.sf.log4jdbc.DriverSpy"));

        setDefaultAutoCommit(StringUtil.getBoolean(getProperty(key, "defaultAutoCommit"), true));

        setInitialSize(StringUtil.getNumber(getProperty(key, "initialSize"), 20).intValue());

        setMaxActive(StringUtil.getNumber(getProperty(key, "maxActive"), 100).intValue());

        setMaxIdle(StringUtil.getNumber(getProperty(key, "maxIdle"), 25).intValue());

        setMinIdle(StringUtil.getNumber(getProperty(key, "minIdle"), 10).intValue());

        setMaxWait(StringUtil.getNumber(getProperty(key, "maxWait"), 20000).longValue());

        setValidationQuery(StringUtil.firstNonEmpty(getProperty(key, "validationQuery"), "select '" + key + "'"));

        setTestOnBorrow(StringUtil.getBoolean(getProperty(key, "testOnBorrow"), true));

        setTestOnReturn(StringUtil.getBoolean(getProperty(key, "testOnReturn"), false));

        setTestWhileIdle(StringUtil.getBoolean(getProperty(key, "testWhileIdle"), true));

        setTimeBetweenEvictionRunsMillis(StringUtil.getNumber(getProperty(key, "timeBetweenEvictionRunsMillis"), 60000).longValue());

        setNumTestsPerEvictionRun(StringUtil.getNumber(getProperty(key, "numTestsPerEvictionRun"), 5).intValue());

        setMinEvictableIdleTimeMillis(StringUtil.getNumber(getProperty(key, "minEvictableIdleTimeMillis"), 1800000).longValue());

        setUrl(getProperty(key, "url"));

        setUsername(getProperty(key, "username"));

        setPassword(getProperty(key, "password"));

        DATA_SOURCES.put(dataSourceType, this);

//        Monitors.addServiceMonitor(new DataSourceMonitor(this, dataSourceType.getKey(), dataSourceType.getName()));
    }

    // {0} - platform config prefix
    // {1} - data source key
    // {2} - config key.
    private static String[] CONFIG_KEYS = new String[]{
            "{0}.{1}.{2}",
            "{0}.{2}",
            "{1}.{2}",
            "{2}",
    };

    private static String getProperty(String dataSourceKey, String... configKeys){

        /*
        for(String k : CONFIG_KEYS){
            int keyIndex = 0;
            for(String configKey : configKeys){
                keyIndex ++;
                String key = MessageFormat.format(k, CONFIG_KEY_PREFIX, dataSourceKey, configKey);
                String value = DBProperties.getString(key);
                if(!StringUtil.isEmpty(value)){
                    String logValue = value;
                    if(String.valueOf(key).indexOf("password") != -1){
                        logValue = "************************";
                    }
                    LOGGER.info("For dataSourceKey=", dataSourceKey, " configKey=", configKey, " ---- found config key=", key, " value=", logValue);
                    if(keyIndex > 1){
                        LOGGER.warn("Using legacy platform db config key=", key, " value=", logValue, "-- You must use new config key:", MessageFormat.format(k, CONFIG_KEY_PREFIX, dataSourceKey, configKeys[0]));
                    }
                    return value;
                }
            }
        }
        */
//        LOGGER.trace("No config value found For dataSourceKey=", dataSourceKey, " configKeys=", configKeys, " ---- returning default value: =", null);
//        String value = ConfigLoader.getString(dataSourceKey + "." + configKeys[0]);
        String value = Configuration.getConfig(dataSourceKey + "." + configKeys[0]).getStringValue();
        return value;
    }

    private static int unKnownCounter = 0;

    public YoutillsDataSource() {
        showConnection();
        this.dataSourceType = null;
        int counter = unKnownCounter++;
//        Monitors.addServiceMonitor(new DataSourceMonitor(this, "Unknown: " + counter, "Unknown:" + counter));
    }

//    public IDataSourceType getDataSourceType() {
//        return dataSourceType;
//    }


    private static final AtomicInteger counter = new AtomicInteger(1);

    private void showConnection() {

        final Date now = new Date();
        final int num = counter.getAndAdd(1);
        Thread t = new Thread(new Runnable(){
            public void run() {
                try {
                    Thread.sleep((num * 10) + 2000L ); //adding num * 10 just to make sure the first one prints first.
                } catch (InterruptedException e) {
                    //ignored
                }
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
//                System.out.println("\n" + df.format(now) + ": Connection Pool #" + num + ", url= " + getUrl() + " (user=" + getUsername() + ")\n");
            }
        });
        t.setName("BDS-Thread-"+num);
        t.start();
    }

    public <T> T unwrap(Class<T> clazz) throws SQLException {
        return null;
    }

    public boolean isWrapperFor(Class<?> clazz) throws SQLException {
        return false;
    }


    public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
         throw new   SQLFeatureNotSupportedException();
    }
}
